﻿using System;
using System.Collections;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using FireStarter.Base;
using Firestarter.DB.Schema.BO;
using Habanero.Base;
using Habanero.Base.Exceptions;
using Habanero.BO;
using Habanero.Faces.Base;
using Habanero.Faces.Win;
using DockStyle = Habanero.Faces.Base.DockStyle;
using FormBorderStyle = Habanero.Faces.Base.FormBorderStyle;
using FormStartPosition = Habanero.Faces.Base.FormStartPosition;

namespace Firestarter.DB.Schema.UI
{

    public class DatabaseInfoContextMenuController
    {
        public const string MERGE_RENAMED = "Merge renamed {0}...";
        public const string MENUITEM_TABLE = "table";
        public const string MENUITEM_COLUMN = "column";

        private IMainMenuHabanero _treeViewContextMenu;
        public DBDatabase Database { get; set; }
        public TreeViewWin TreeView { get; private set; }
        public IMainMenuHabanero TreeViewContextMenu
        {
            get { return _treeViewContextMenu; }
        }

        public DatabaseInfoContextMenuController(TreeViewWin treeView)
        {
            TreeView = treeView;
            TreeView.MouseClick += MouseClickEventHandler;
        }

        private void MouseClickEventHandler(object sender, MouseEventArgs e)
        {

            if (TreeView == null) return;
            if (e.Button != System.Windows.Forms.MouseButtons.Right) return;

            Point clickLocation = e.Location;
            ITreeNode node = GetTreeNode(TreeView, clickLocation);
            if (node == null) return;
            TreeView.SelectedNode = node;

            BindContextMenu(node);

            ShowContextMenu(TreeView, clickLocation);
        }

        protected virtual void ShowContextMenu(TreeViewWin treeView, Point clickLocation)
        {
            ((ContextMenu)_treeViewContextMenu).Show(treeView, clickLocation);
        }

        protected virtual ITreeNode GetTreeNode(TreeViewWin treeView, Point clickLocation)
        {
            return treeView.GetNodeAt(clickLocation) as ITreeNode;
        }

        private void BindContextMenu(ITreeNode node)
        {

            var selectedBo = node.Tag as IMergableBo;

            SetupMenuItem(selectedBo);
        }

        public void SetupMenuItem(IMergableBo selectedBo)
        {
            //In the future if more menu items are added or support for more BO Types is required then
            // you probably want to use a strategy pattern see  GetAvailableBOs

            SetupContextMenu(); // This is called in the mouseClick event to rebuild the context menu
            // We need to do this to ensure that we don't end up attaching multiple events to the same
            // menu item event handler.

            DisableAllMenuItems();
            IMenuItem menuItem = GetMenuItem(selectedBo);
            if (menuItem == null) return;
            menuItem.Enabled = MenuItemShouldBeEnabled(selectedBo);
            menuItem.Click += delegate { ShowRenamedBoDialog(selectedBo); };
        }

        public IMenuItem GetMenuItem(IMergableBo selectedBo)
        {
            string menuItemName = GetMenuItemName(selectedBo);
            return FindMenuItemByName(menuItemName);
        }

        public static string GetMenuItemName(IMergableBo bo)
        {
            return string.Format(MERGE_RENAMED, GetBoDisplayText(bo));
        }

        private static string GetBoDisplayText(IMergableBo bo)
        {
            var table = bo as DBTable;
            if (table != null)
            {
                return MENUITEM_TABLE;
            }

            var column = bo as DBColumn;
            if (column != null)
            {
                return MENUITEM_COLUMN;
            }
            
            throw new HabaneroDeveloperException("Only DBTable and DBColumn can be used.");
        }

        public static bool MenuItemShouldBeEnabled(IMergableBo bo)
        {
            if (bo.PendingChangeType == ChangeType.Add || bo.PendingChangeType == ChangeType.Delete)
            {
                return true;
            }
            return false;
        }
        //Do not think you need to create both menu items but I can live with it
        private void SetupContextMenu()
        {
            var menu = new HabaneroMenu("TreeViewContext");
            menu.AddMenuItem(string.Format(MERGE_RENAMED, MENUITEM_TABLE));
            menu.AddMenuItem(string.Format(MERGE_RENAMED, MENUITEM_COLUMN));

            var contextMenuBuilderWin = new ContextMenuBuilderWin(GlobalUIRegistry.ControlFactory);
            contextMenuBuilderWin.BuildMainMenu(menu);
            _treeViewContextMenu = contextMenuBuilderWin.BuildMainMenu(menu);
        }

        private IMenuItem FindMenuItemByName(string menuItemName)
        {
            // Only need a simple search as the context menu currently doesn't have sub-menu's
            if (_treeViewContextMenu == null || _treeViewContextMenu.MenuItems.Count == 0) return null;

            return _treeViewContextMenu.MenuItems.FirstOrDefault(item => item.Text == menuItemName);
        }

        private static void ShowRenamedBoDialog(IMergableBo selectedBo)
        {

            if (selectedBo == null) return;

            var factory = GlobalUIRegistry.ControlFactory;
            var form = CreateForm(factory);

            var borderLayoutManager = factory.CreateBorderLayoutManager(form);

            var dialogControl = new MergeBOsControl()
                                {
                                    Dock = (System.Windows.Forms.DockStyle)DockStyle.Fill,
                                    BoSelectedInTree = selectedBo
                                };

            borderLayoutManager.AddControl(dialogControl);

            dialogControl.MergeableBoCombo.BusinessObjectCollection = GetPotentialRenamedBOsFor(selectedBo);
            if (dialogControl.MergeableBoCombo.BusinessObjectCollection == null) return;
            // if (dialogControl.MergeableBoCombo.BusinessObjectCollection == null) show No items Message and return
            //dialogControl.MergeableBoCombo.SelectedIndex = 0;
            form.Text = "Merge renamed item";
            form.ShowDialog();
        }

        private void DisableAllMenuItems()
        {
            if (_treeViewContextMenu == null) return;
            foreach (IMenuItem menuItem in (IEnumerable)_treeViewContextMenu.MenuItems)
            {
                menuItem.Enabled = false;
            }
        }

        private static IFormHabanero CreateForm(IControlFactory factory)
        {
            IFormHabanero form = factory.CreateForm();
            form.Size = new System.Drawing.Size(280, 130);
            form.FormBorderStyle = FormBorderStyle.FixedDialog;
            form.StartPosition = FormStartPosition.CenterParent;
            return form;
        }


        public static IBusinessObjectCollection GetPotentialRenamedBOsFor(IMergableBo selectedBo)
        {
            IBusinessObjectCollection availableBOs = GetAvailableBOs(selectedBo);
            return GetPotentialRenamedBOsFrom(availableBOs, selectedBo.ClassDef, selectedBo.PendingChangeType);
        }

        private static IBusinessObjectCollection GetAvailableBOs(IMergableBo selectedBo)
        {
            //If you ever need to extend this to more items then I would suggest that 
            // you extract this method and GetMenuItemName method into a 
            // strategy pattern.
            var selectedColumnBo = selectedBo as DBColumn;
            if (selectedColumnBo != null) return GetAvailableBOs(selectedColumnBo);

            var selectedTableBo = selectedBo as DBTable;
            if (selectedTableBo != null) return GetAvailableBOs(selectedTableBo);

            throw new HabaneroDeveloperException("Only DBTable and DBColumn mergableBo's should be evaluated.");
        }

        private static IBusinessObjectCollection GetAvailableBOs(DBTable selectedTableBo)
        {
            return selectedTableBo.Database.Tables;
        }
        private static IBusinessObjectCollection GetAvailableBOs(DBColumn selectedColumn)
        {
            return selectedColumn.Table.Columns;
        }

        private static IBusinessObjectCollection GetPotentialRenamedBOsFrom(IBusinessObjectCollection availableBOs, IClassDef classDef, ChangeType pendingChangeType)
        {
            ChangeType changeType = GetRenamingChangeTypeForSelectedBo(pendingChangeType);
            var potentialRenamedBOs = CreateCollectionOfType(classDef);
            foreach (IMergableBo availableBO in availableBOs)
            {
                if (availableBO.PendingChangeType == changeType) potentialRenamedBOs.Add(availableBO);
            }
            return potentialRenamedBOs;
        }

        private static ChangeType GetRenamingChangeTypeForSelectedBo(ChangeType pendingChangeType)
        {
            if (pendingChangeType == ChangeType.Add)
            {
                return ChangeType.Delete;
            }

            if (pendingChangeType == ChangeType.Delete)
            {
                return ChangeType.Add;
            }

            throw new HabaneroDeveloperException("Only ChangeTypes of Add and Delete are valid in renaming scenarios.");
        }
                /// <summary>
        /// Creates a Generic Collection of <see cref="IBusinessObjectCollection"/> of the Generic
        /// Type determined by the <paramref name="classDef"/>
        /// </summary>
        /// <param name="classDef">The ClassDef to use for the collection (and its <see cref="SelectQuery"/>)</param>
        /// <returns></returns>
        protected static IBusinessObjectCollection CreateCollectionOfType(IClassDef classDef)
        {
            Type boColType = typeof(BusinessObjectCollection<>).MakeGenericType(classDef.ClassType);
            return (IBusinessObjectCollection) Activator.CreateInstance(boColType, classDef);
        }
    }

}
